package com.social.service.impl;

import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.social.dao.UserDao;
import com.social.dao.SysMenuDao;
import com.social.dao.SysRoleDao;
import com.social.pojo.*;
import com.social.pojo.dto.SmsDTO;
import com.social.service.SysRoleService;
import com.social.service.SysUserRoleService;
import com.social.service.SysUserService;
import com.social.utils.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpSession;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.social.constant.MessageConstant.DEFAULT_PASSWORD;
import static com.social.constant.RabbitMqConstants.EXCHANGE_TOPIC_SMS;
import static com.social.constant.RabbitMqConstants.ROUTING_SMS_ITEM;
import static com.social.constant.RedisConstants.*;

/**
 * @BelongsProject: social
 * @BelongsPackage: com.social.service.impl
 * @Author: zhm
 * @CreateTime: 2022-09-26  20:44
 * @Description: TODO
 * @Version: 1.0
 */
@Slf4j
@Service
public class SysUserServiceImpl extends ServiceImpl<UserDao, SysUser> implements SysUserService {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private UserDao userDao;

    @Autowired
    private SysRoleDao sysRoleDao;

    @Autowired
    private SysMenuDao sysMenuMapper;

    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;

    @Autowired
    private SysRoleService sysRoleService;

    @Autowired
    private SysUserRoleService sysUserRoleService;
    @Resource
    private AmqpTemplate amqpTemplate;


    @Override
    public Result register(SysUser sysUser, HttpSession session) {
        // 校验手机号
        if (RegexUtils.isPhoneInvalid(sysUser.getPhone())){
            return Result.fail(201, "手机号格式错误！");
        }
        // 2. 校验验证码
        Object cacheCode = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY + sysUser.getPhone());
        System.out.println(LOGIN_CODE_KEY + sysUser.getPhone());
        System.out.println("cacheCode:" + cacheCode);
        String code = sysUser.getCode();
        System.out.println("code:" + code);
        if (cacheCode == null || !cacheCode.toString().equals(code)){
            return Result.fail(201,"验证码错误");
        }
        // 查询条件
        QueryWrapper<SysUser> wrapper = Wrappers.query();
        wrapper.eq("phone", sysUser.getPhone());
        SysUser presult = getOne(wrapper);
        SysUser result = query().eq("username", sysUser.getUsername()).one();
        System.out.println(result);
        if (result != null) {
            return Result.fail(201, "用户名被占用");
        }
        if (presult != null) {
            return Result.fail(201, "手机号已经注册");
        }

        if(sysUser.getPassword().equals(sysUser.getNewPassword())){
            sysUser.setPassword(bCryptPasswordEncoder.encode(sysUser.getNewPassword()));
            sysUser.setUpdateTime(new Date());
            boolean save = save(sysUser);
            System.out.println(sysUser.getPhone());
            if (save == false) {
                return Result.fail(201, "注册失败");
            }
            return Result.ok(200);
        } else{
            return Result.fail(201, "两次密码不一样！");
        }

    }

    @Override
    public Result login(SysUser user, HttpSession httpSession) {
        SysUser sysUser = findByUsername(user.getUsername());
        if (sysUser == null) {
            return Result.fail(201, "用户名或密码错误！");
        }
        if ("1".equals(sysUser.getStatus())) {
            return Result.fail(202, "改账户以被封禁请联系管理员！");
        }
        if (!user.getPassword().equals(bCryptPasswordEncoder.encode(sysUser.getPassword()))) {
            System.out.println("test");
            return Result.fail(201, "用户名或密码错误！");
        }
        return Result.ok(200);
    }

    @Override
    public Result sendCode(String phone, HttpSession session) {
        System.out.println("phone" + phone);
        // 校验手机号
        if (RegexUtils.isPhoneInvalid(phone)) {
            // 如果不符合，返回错误信息
            return Result.fail(201, "手机号格式错误！");
        }
        // 符合，生成验证码
        String code = RandomUtil.randomNumbers(6);
        SmsDTO smsDTO = new SmsDTO();
        smsDTO.setMessage(code);
        smsDTO.setMobile(phone);
        amqpTemplate.convertAndSend(EXCHANGE_TOPIC_SMS, ROUTING_SMS_ITEM, smsDTO);
        stringRedisTemplate.opsForValue().set(LOGIN_CODE_KEY + phone, code, LOGIN_CODE_TTL, TimeUnit.MINUTES);
//        Map<String, Object> map = new HashMap<>();
//        map.put("code", code);
//        boolean isSend = messageService.send(map, phone);
//
//        if (isSend) {
//            // 保存验证码到redis
//            stringRedisTemplate.opsForValue().set(LOGIN_CODE_KEY + phone, code, LOGIN_CODE_TTL, TimeUnit.MINUTES);
//
//            // 发送验证码
//            log.info("发送短信验证码成功，验证码：{}", code);
//
//            return Result.ok(200);
//        } else {
//            return Result.fail(201, "短信发送失败！");
//        }
        return Result.ok(200,"验证码发送中");
    }

    @Override
    public SysUser findByUsername(String username) {
        String key = LOGIN_USER_KEY + username;

        String Json = stringRedisTemplate.opsForValue().get(key);

        if (StringUtil.isNotEmpty(Json)) {
            return JSONUtil.toBean(Json, SysUser.class);
        }

        QueryWrapper<SysUser> wrapper = Wrappers.query();
        wrapper.eq("username", username);

        SysUser sysUser = getOne(wrapper);

        String jsonStr = JSONUtil.toJsonStr(sysUser);
        stringRedisTemplate.opsForValue().set(key, jsonStr);
        // 设置token有效期
        stringRedisTemplate.expire(key, LOGIN_USER_TTL, TimeUnit.MINUTES);
        return sysUser;
    }

    @Override
    public SysUser findByPhone(String phone) {
        String key = LOGIN_USER_KEY + phone;

        String json = stringRedisTemplate.opsForValue().get(key);

        if (json != null) {
            return JSONUtil.toBean(json, SysUser.class);
        }

        QueryWrapper<SysUser> wrapper = Wrappers.query();
        wrapper.eq("phone", phone);

        SysUser sysUser = getOne(wrapper);
        if (sysUser == null) {
            return null;
        }
        String jsonStr = JSONUtil.toJsonStr(sysUser);
        stringRedisTemplate.opsForValue().set(key, jsonStr);
        // 设置token有效期
        stringRedisTemplate.expire(key, LOGIN_USER_TTL, TimeUnit.MINUTES);
        return sysUser;
    }

    @Override
    public String getUserAuthorityInfo(Long userId) {

        System.out.println("测试用户id"+userId);
        String key = ROLE_USER_KEY + userId;
        String str = stringRedisTemplate.opsForValue().get(key);
        if (StringUtil.isNotEmpty(str)) {
            System.out.println("zhm测试authority:"+str);
            return str;
        }

        StringBuffer authority=new StringBuffer();
        // 根据用户id获取所有的角色信息
        List<SysRole> roleList = sysRoleDao
                .selectList(new QueryWrapper<SysRole>().inSql("id", "SELECT role_id FROM sys_user_role WHERE user_id=" + userId));
        if(roleList.size()>0){
            String roleCodeStrs = roleList
                    .stream().map(r -> "ROLE_" + r.getCode()).collect(Collectors.joining(","));
            authority.append(roleCodeStrs);
        }
        // 遍历所有的角色，获取所有菜单权限 而且不重复
        Set<String> menuCodeSet=new HashSet<>();
        for(SysRole sysRole:roleList){
            List<SysMenu> sysMenuList = sysMenuMapper
                    .selectList(new QueryWrapper<SysMenu>()
                            .inSql("id", "SELECT menu_id FROM sys_role_menu WHERE role_id=" + sysRole.getId()));
            for(SysMenu sysMenu:sysMenuList){
                String perms=sysMenu.getPerms();
                if(StringUtil.isNotEmpty(perms)){
                    menuCodeSet.add(perms);
                }
            }
        }
        if(menuCodeSet.size()>0){
            authority.append(",");
            String menuCodeStrs = menuCodeSet.stream().collect(Collectors.joining(","));
            authority.append(menuCodeStrs);
        }
        stringRedisTemplate.opsForValue().set(key, authority.toString(), ROLE_MENU_TTL, TimeUnit.MINUTES);
        System.out.println("zhm测试authority:"+authority.toString());
        return authority.toString();
    }

    @Override
    public Result add(SysUser sysUser) {
        if(sysUser.getId() == null || sysUser.getId() == -1){
            sysUser.setCreateTime(new Date());
            sysUser.setPassword(bCryptPasswordEncoder.encode(sysUser.getPassword()));
            save(sysUser);
        }else{
            sysUser.setUpdateTime(new Date());
            updateById(sysUser);
        }
        return Result.ok(200);
    }

    @Override
    public Result updateUserPwd(SysUser sysUser) {
        SysUser currentUser = getById(sysUser.getId());
        if(bCryptPasswordEncoder.matches(sysUser.getOldPassword(),currentUser.getPassword())){
            currentUser.setPassword(bCryptPasswordEncoder.encode(sysUser.getNewPassword()));
            currentUser.setUpdateTime(new Date());
            updateById(currentUser);
            return Result.ok(200);
        }else{
            return Result.fail(201, "输入旧密码错误！");
        }
    }

    @Override
    public Result updateAvatar(SysUser sysUser) {
        SysUser currentUser = getById(sysUser.getId());
        currentUser.setUpdateTime(new Date());
        currentUser.setAvatar(sysUser.getAvatar());
        updateById(currentUser);
        return Result.ok(200);
    }

    @Override
    public Result findAll(PageBean pageBean) {
        String query = pageBean.getQuery().trim();
        Page<SysUser> pageResult = page(new Page<>(pageBean.getPageNum(), pageBean.getPageSize()),new QueryWrapper<SysUser>()
                .like(StringUtil.isNotEmpty(query),"username",query));
        List<SysUser> userList = pageResult.getRecords();
        for(SysUser user:userList){
            Wrapper<SysRole> wrapper = new QueryWrapper<SysRole>()
                    .inSql("id", "select role_id from sys_user_role where user_id=" + user.getId());
            List<SysRole> roleList = sysRoleService.list(wrapper);
            user.setSysRoleList(roleList);
        }
        Map<String,Object> resultMap=new HashMap<>();
        resultMap.put("userList",userList);
        resultMap.put("total",pageResult.getTotal());

        return Result.ok(200, resultMap);
    }

    @Override
    public Result checkUserName(SysUser sysUser) {
        if(findByUsername(sysUser.getUsername()) == null){
            return Result.ok(200);
        }else{
            return Result.fail(201, "用户名错误！");
        }
    }

    @Override
    public Result resetPassword(Integer id) {
        SysUser sysUser = getById(id);
        sysUser.setPassword(bCryptPasswordEncoder.encode(DEFAULT_PASSWORD));
        sysUser.setUpdateTime(new Date());
        updateById(sysUser);
        return Result.ok(200);
    }

    @Override
    public Result updateStatus(Integer id, String status) {
        SysUser sysUser = getById(id);
        sysUser.setStatus(status);
        saveOrUpdate(sysUser);
        return Result.ok(200);
    }

    @Override
    public Result grantRole(Long userId, Long[] roleIds) {
        List<SysUserRole> userRoleList=new ArrayList<>();
        Arrays.stream(roleIds).forEach(r -> {
            SysUserRole sysUserRole = new SysUserRole();
            sysUserRole.setRoleId(r);
            sysUserRole.setUserId(userId);
            userRoleList.add(sysUserRole);
        });
        sysUserRoleService.remove(new QueryWrapper<SysUserRole>().eq("user_id",userId));
        sysUserRoleService.saveBatch(userRoleList);
        return Result.ok(200);
    }

    @Override
    public Result getKey() {
        String jsonStr= stringRedisTemplate.opsForValue().get(AES_KEY_KEY);
        if (StrUtil.isNotBlank(jsonStr)) {
            return Result.ok(200, jsonStr);
        }
        String key = AESUtil.getKey();
        stringRedisTemplate.opsForValue().set(AES_KEY_KEY, key, AES_KEY_TTL, TimeUnit.DAYS);
        return Result.ok(200, key);
    }


}
